//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_watch_api_serv

import (
	"context"
	"fmt"
	"time"

	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/comment_dao"
	"atomgit.com/openlinksaas/api-server/dao/issue_dao"
	"atomgit.com/openlinksaas/api-server/dao/project_entry_dao"
	"atomgit.com/openlinksaas/api-server/dao/requirement_dao"
	"atomgit.com/openlinksaas/api-server/dao/testcase_dao"
	"atomgit.com/openlinksaas/api-server/dao/watch_dao"
	"atomgit.com/openlinksaas/api-server/utils"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_watch_api"
	"go.mongodb.org/mongo-driver/mongo"
)

type ProjectWatchApiImpl struct {
	project_watch_api.UnimplementedProjectWatchApiServer
}

func (apiImpl *ProjectWatchApiImpl) Watch(ctx context.Context, req *project_watch_api.WatchRequest) (*project_watch_api.WatchResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_watch_api.WatchResponse{
			Code:   project_watch_api.WatchResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_watch_api.WatchResponse{
			Code:   project_watch_api.WatchResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_watch_api.WatchResponse{
			Code:   project_watch_api.WatchResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_watch_api.WatchResponse{
			Code:   project_watch_api.WatchResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	//检查是否已经关注
	exist, err := watch_dao.MyWatchDao.Exist(ctx, req.ProjectId, sessionItem.UserId, req.TargetId, uint32(req.TargetType))
	if err != nil {
		return nil, err
	}
	if exist {
		return &project_watch_api.WatchResponse{
			Code: project_watch_api.WatchResponse_CODE_OK,
		}, nil
	}
	//检查目标是否存在
	targetTitle, err := apiImpl.getTargetTitle(ctx, req.ProjectId, req.TargetType, req.TargetId)
	if err != nil {
		return &project_watch_api.WatchResponse{
			Code:   project_watch_api.WatchResponse_CODE_NO_TARGET,
			ErrMsg: "关注目标不存在",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = watch_dao.MyWatchDao.Insert(sessCtx, &watch_dao.MyWatchInfoDbItem{
			ProjectId:  req.ProjectId,
			UserId:     sessionItem.UserId,
			TargetId:   req.TargetId,
			TargetType: uint32(req.TargetType),
			WatchTime:  nowTime,
		})
		if err != nil {
			return nil, err
		}
		return &project_watch_api.WatchResponse{
			Code: project_watch_api.WatchResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addWatchEvent(sessionItem, req, projectItem, targetTitle, nowTime)
	return ret.(*project_watch_api.WatchResponse), nil
}

func (apiImpl *ProjectWatchApiImpl) getTargetTitle(ctx context.Context, projectId string, targetType project_watch_api.WATCH_TARGET_TYPE, targetId string) (string, error) {
	if targetType == project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_ENTRY {
		item, err := project_entry_dao.EntryDao.Get(ctx, targetId, projectId)
		if err != nil {
			return "", err
		}
		return item.EntryTitle, nil
	} else if targetType == project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT {
		item, err := requirement_dao.RequirementInfoDao.Get(ctx, projectId, targetId)
		if err != nil {
			return "", err
		}
		return item.BaseInfo.Title, nil
	} else if targetType == project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_TASK || targetType == project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_BUG {
		item, err := issue_dao.IssueDao.Get(ctx, projectId, targetId)
		if err != nil {
			return "", err
		}
		return item.BasicInfo.Title, nil
	} else if targetType == project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_TEST_CASE {
		item, err := testcase_dao.CaseInfoDao.Get(ctx, targetId, projectId)
		if err != nil {
			return "", err
		}
		return item.Title, nil
	}
	return "", fmt.Errorf("unkwown target type")
}

func (apiImpl *ProjectWatchApiImpl) Unwatch(ctx context.Context, req *project_watch_api.UnwatchRequest) (*project_watch_api.UnwatchResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_watch_api.UnwatchResponse{
			Code:   project_watch_api.UnwatchResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_watch_api.UnwatchResponse{
			Code:   project_watch_api.UnwatchResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_watch_api.UnwatchResponse{
			Code:   project_watch_api.UnwatchResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_watch_api.UnwatchResponse{
			Code:   project_watch_api.UnwatchResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	//检查是否已经关注
	exist, err := watch_dao.MyWatchDao.Exist(ctx, req.ProjectId, sessionItem.UserId, req.TargetId, uint32(req.TargetType))
	if err != nil {
		return nil, err
	}
	if !exist {
		return &project_watch_api.UnwatchResponse{
			Code: project_watch_api.UnwatchResponse_CODE_OK,
		}, nil
	}
	targetTitle, _ := apiImpl.getTargetTitle(ctx, req.ProjectId, req.TargetType, req.TargetId) //skip error check

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = watch_dao.MyWatchDao.Remove(sessCtx, req.ProjectId, sessionItem.UserId, req.TargetId, uint32(req.TargetType))
		if err != nil {
			return nil, err
		}
		//删除未读评论信息
		commentTargetType := convertTargetType(req.TargetType)
		err = comment_dao.UnReadDao.Remove(sessCtx, req.ProjectId, sessionItem.UserId, uint32(commentTargetType), req.TargetId)
		if err != nil {
			return nil, err
		}
		return &project_watch_api.UnwatchResponse{
			Code: project_watch_api.UnwatchResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addUnwatchEvent(sessionItem, req, projectItem, targetTitle, time.Now().UnixNano()/1e6)
	return ret.(*project_watch_api.UnwatchResponse), nil
}

func (apiImpl *ProjectWatchApiImpl) ListMyWatch(ctx context.Context, req *project_watch_api.ListMyWatchRequest) (*project_watch_api.ListMyWatchResponse, error) {
	emptyList := []*project_watch_api.MyWatchInfo{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_watch_api.ListMyWatchResponse{
			Code:     project_watch_api.ListMyWatchResponse_CODE_WRONG_SESSION,
			ErrMsg:   "会话不存在",
			InfoList: emptyList,
		}, nil
	}
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_watch_api.ListMyWatchResponse{
			Code:     project_watch_api.ListMyWatchResponse_CODE_NO_PROJECT,
			ErrMsg:   "项目不存在",
			InfoList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_watch_api.ListMyWatchResponse{
			Code:     project_watch_api.ListMyWatchResponse_CODE_NO_PERMISSION,
			ErrMsg:   "没有权限",
			InfoList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	count, err := watch_dao.MyWatchDao.Count(ctx, req.ProjectId, sessionItem.UserId, req.FilterByTargetType, uint32(req.TargetType))
	if err != nil {
		return nil, err
	}
	myWatchItemList, err := watch_dao.MyWatchDao.List(ctx, req.ProjectId, sessionItem.UserId, req.FilterByTargetType, uint32(req.TargetType), int64(req.Offset), int64(req.Limit))
	if err != nil {
		return nil, err
	}
	//获取标题
	entryIdList := []string{}
	reqIdList := []string{}
	issueIdList := []string{}
	for _, myWatchItem := range myWatchItemList {
		if myWatchItem.TargetType == uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_ENTRY) {
			entryIdList = append(entryIdList, myWatchItem.TargetId)
		}
		if myWatchItem.TargetType == uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT) {
			reqIdList = append(reqIdList, myWatchItem.TargetId)
		}
		if myWatchItem.TargetType == uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_TASK) || myWatchItem.TargetType == uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_BUG) {
			issueIdList = append(issueIdList, myWatchItem.TargetId)
		}
	}

	titleMap := map[string]string{}
	if len(entryIdList) > 0 {
		itemList, err := project_entry_dao.EntryDao.ListById(ctx, entryIdList)
		if err != nil {
			return nil, err
		}
		for _, item := range itemList {
			titleMap[item.EntryId] = item.EntryTitle
		}
	}
	if len(reqIdList) > 0 {
		itemList, err := requirement_dao.RequirementInfoDao.ListById(ctx, req.ProjectId, reqIdList)
		if err != nil {
			return nil, err
		}
		for _, item := range itemList {
			titleMap[item.RequirementId] = item.BaseInfo.Title
		}
	}
	if len(issueIdList) > 0 {
		itemList, err := issue_dao.IssueDao.ListById(ctx, req.ProjectId, issueIdList)
		if err != nil {
			return nil, err
		}
		for _, item := range itemList {
			titleMap[item.IssueId] = item.BasicInfo.Title
		}
	}
	//生成结果
	infoList := []*project_watch_api.MyWatchInfo{}
	for _, myWatchItem := range myWatchItemList {
		info := myWatchItem.ToMyWatchInfo()
		info.Title = titleMap[myWatchItem.TargetId]
		infoList = append(infoList, info)
	}

	return &project_watch_api.ListMyWatchResponse{
		Code:       project_watch_api.ListMyWatchResponse_CODE_OK,
		TotalCount: count,
		InfoList:   infoList,
	}, nil
}
